﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System.Runtime.Serialization;
using System.Text;
using System;
using System.Threading;
using System.ComponentModel;
using System.Windows;
using System.Collections;
using System.Collections.Generic;
using BrainTechLLC.ThreadSafeObjects;
#if NO_SILVERLIGHT
using LinFu.AOP.Interfaces;
using BrainTechLLC.Logging;
using System.Collections.Specialized;
using System.Xml;
using System.Xml.Serialization;
using System.Collections.ObjectModel;
using Mud.EmlenMudMain;
#endif
#if USE_HYPER
using Hyper.ComponentModel;
#endif

namespace BrainTechLLC
{    
#if NO_SILVERLIGHT
    [Browsable(true)]
    [TypeConverter(typeof(ExpandableObjectConverter))]
    [EditorBrowsable(EditorBrowsableState.Always)]
    [AOPEnabled]
    [KnownType("ReturnAllAbilityTypes")]    
#endif
    [Serializable]
    [DataContract]
    public partial class GameChar : GameThingBaseClass, IKeywordAccessible, IGameThing,
        IChangeable, INotifiable, ICanBeNotified, IHasPropertyData, ICanBePlacedInWorld,
        IHasLocationInt, IHasNameAndDescription, IRaisesNotifications, ICanReceiveCommands,
        INotifyPropertyChanged, IViewableThing,
        ICanObserveViewable, IEventPerceiver, ICanBeInfluenced, INotifyPropertyChanging
    {
        [NonSerialized]
        public static readonly int CommandToExecutionDelay = 50;

        [NonSerialized]
        public static readonly int ReturnAttackDelay = 200;

#if NO_SILVERLIGHT
        public static Collection<Type> ReturnAllAbilityTypes()
        {
            return Ability.ReturnAllAbilityTypes();
        }
#endif

        public GameChar() : base() { }

        public override bool IssueCommand(ICommandToPerform command)
        {
            Globals.SchedulingEngine.ScheduleEventToExecute(command as CommandToPerform, CommandToExecutionDelay);
            return true;
        }

        public void EnqueueModification(SingleModification mod)
        {
            ChangesAndActions.ModificationsAndRequisites.Enqueue(new Modification() { Change = mod });
        }

        public void ToGameChar(IGameThing thing)
        {
            if (InfluenceManager.RegisterInfluence(thing as ICanRegisterInfluence, this))
            {
                //Dependencies.AddOrSet(thing.AssociatedID, thing);
            }
        }

        [NonSerialized]
        public PlayerSettingsData _settings;

        [NonSerialized]
        public static readonly PlayerSettingsData DefaultSettings = new PlayerSettingsData(new IdentifierAndValue("LookAfterMove", true));

        public PlayerSettingsData Settings
        {
            get
            {
                if (_settings == null)
                    return DefaultSettings;

                return _settings;
            }
        }

        public override void PerformPostRestoreSetup()
        {
            base.PerformPostRestoreSetup();
            this.ExecuteChangesAndActions(500);
        }

        public void FromGameChar(IGameThing thing)
        {
            if (InfluenceManager.RemoveInfluence(thing as ICanRegisterInfluence, this))
            {
                //Dependencies.Remove(thing.AssociatedID);
            }
        }

        public bool IsDead
        {
            get { return (_properties != null && GetProperty<int>("HitPoints") <= 0); }
        }

        public override void PlaceInWorld(List<object> arguments)
        {
            if (!_isPlaced)
            {
                _isPlaced = true;

                LocationInt location = (arguments == null) ? LocationInt : arguments[0] as LocationInt;

                if (WorldMap.Instance.FindLocationOfThingID(AssociatedID) == null)
                {
                    WorldMap.Instance.ToLocation(AssociatedID, location);
                    var loc = WorldMap.Instance.World[location.x, location.y, location.z];
                    
                    if (loc != null)
                    {
                        Location = loc.AssociatedID;
                    }
                }
            }
        }

        public bool Attacking
        {
            get
            {
                State state = States.FindStateByName("BasicAttackState");
                return (state != null);
            }
        }

        public bool BeingAttacked
        {
            get
            {
                State state = States.FindStateByName("BeingAttackedState");
                return (state != null);
            }
        }


        //public long Attacking
        //{
        //    get
        //    {
        //        State state = States.FindStateByName("BasicAttackState");                
        //        return (state == null) ? 0 : state.ThingProperties.GetArgument<long>("TargetID");
        //    }
        //}


        //public void RegisterAttackFrom(long attackerID)
        //{
        //    if (!Attackers.ContainsKey(attackerID))
        //        Attackers.Add(attackerID);            

        //    if (Attacking == 0)
        //    {
        //        // Auto-attack back
        //        State attack = StateFactory.CreateBasicAttackState(this.AssociatedID, attackerID, ReturnAttackDelay); // HEREHERE
        //        States.AddState(attack, CommandToExecutionDelay); // HEREHERE
        //    }
        //}

        //public bool StillBeingAttackedBy(long attackerID)
        //{
        //    GameChar chr = Everything.Find<GameChar>(attackerID);
        //    State state = chr.States.FindStateByName("BasicAttackState");

        //    if (state != null && state.ThingProperties.GetArgument<int>("TargetID") == AssociatedID)
        //        return true;

        //    return false;
        //}

        public void ExecuteChangesAndActions(int rescheduleMS)
        {
            if (_changesAndActions != null)
                _changesAndActions.Execute(this);

            if (rescheduleMS > 0)
                Globals.SchedulingEngine.ScheduleEventToExecute(() => this.ExecuteChangesAndActions(rescheduleMS), rescheduleMS);
        }

        public void RegisterChangeOrAction(Modification c)
        {
            ChangesAndActions.ModificationsAndRequisites.Enqueue(c);
        }

        public void AddNotification(INotificationMessage c)
        {
            Notifications.Enqueue(c);

            if (Notifications.Count > 100)
            {
                List<INotificationMessage> items = new List<INotificationMessage>();
                Notifications.DequeueMultiple(items, 80);
            }
        }

        public override void Notify(INotificationMessage o)
        {
            if (NotificationRaised != null)
                NotificationRaised(this, new ObjectEventArgs<INotificationMessage>(o));
        }

#if NO_SILVERLIGHT
        [ObservableMethod(true)]
#endif
        public object HandleCommandReceived(object command)
        {
            IssueCommand(command as CommandToPerform);
            return null;
        }

        public override string ToString()
        {
            //return Name.ToString();
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(Name.ToString());
            sb.AppendLine(LocationInt.ToString());
            sb.AppendLine(ThingProperties.ToString());
            return sb.ToString();
        }

        public bool IssueCommand(string s)
        {
            CommandToPerform cmd = s.ParseInputText(this);

            if (cmd != null)
            {
                HandleCommandReceived(cmd);
                return true;
            }

            return false;
        }

        #region IViewableThing Members

        public override IViewOfThing GetViewWhenObservedBy(ICanObserveViewable looker, ViewType viewType)
        {
            ViewOfThing view = new ViewOfThing();

            List<IViewedProperty> propValues = looker.GetViewOf(this, "ShortName", viewType);

            if (propValues != null)
                view.ViewedProperties.Add("ShortName", propValues);

            return view;
        }

        #endregion

        #region ICanObserveViewable Members

        public List<IViewedProperty> GetViewOf(IViewableThing thing, string property, ViewType viewType)
        {
            List<IViewedProperty> result = new List<IViewedProperty>();
            GameChar chr = thing as GameChar;
            if (chr != null)
            {
                switch (property)
                {
                    case "ShortDescription":
                    case "Keywords":
                    case "OneLineDescription":
                    case "Name":
                    case "ShortName":
                        object o = chr.GetType().PropertiesForType(true)[property].GetValue(chr, null);

                        if ((viewType & ViewType.See) == ViewType.See)
                        {
                            result.Add(new ViewedProperty()
                            {
                                AbleToView = true,
                                Value = new IdentifierAndValue(property, o),
                                ViewType = ViewType.See
                            });
                        }
                        else
                        {
                            result.Add(new ViewedProperty()
                            {
                                AbleToView = true,
                                Value = new IdentifierAndValue(property, o),
                                ViewType = viewType
                            });
                        }
                        break;
                }
            }
            return result;
        }

        #endregion


        public INotificationMessage PerceiveEvent(IEventToPerceive eventInfo)
        {
            return EventPerceiver.PerceiveEvent(this.AssociatedID, this, eventInfo);
        }

        public bool CanPerceive(IEventToPerceive eventInfo, ViewType view)
        {
            if (eventInfo.Targets.ContainsKey(this.AssociatedID))
            {
                // this character is a target of the event.  Make sure it can perceive the event source(s)
                // TODO: to determine - which source to choose?  Or all/or logic?

            }

            // for now, return true in all cases
            return true;
        }

        public bool CanPerceive(IGameThing thing, ViewType view)
        {
            GameChar targetChar = thing as GameChar;

            if (targetChar != null)
            {
                // TODO: determine if the current character can perceive targetChar, given the view type
                // TODO: HEREHERE
            }

            // for now, return true in all cases
            return true;
        }


        public InfluenceCollection InfluenceCollection
        {
            get
            {
                if (_influenceCollection == null)
                    Interlocked.CompareExchange<InfluenceCollection>(ref _influenceCollection, new InfluenceCollection(), null);

                return _influenceCollection;
            }
        }

        [NonSerialized]
        public EventSubscriptions _subscriptions;

        public EventSubscriptions Subscriptions
        {
            get
            {
                if (_subscriptions == null)
                    Interlocked.CompareExchange<EventSubscriptions>(ref _subscriptions, new EventSubscriptions(AssociatedID), null);
                return _subscriptions;
            }
        }

        public static implicit operator AssociatedIDCollection(GameChar chr)
        {
            return new AssociatedIDCollection(chr.AssociatedID);
        }
    }

    [Flags]
    [DataContract]
    public enum Subscription
    {
        [EnumMember]
        None = 0,
        [EnumMember]
        Log = 1,
        [EnumMember]
        RuntimeErrors = 2,
        [EnumMember]
        InGameEvents = 4
    }

    [DataContract]
    public enum OnOffToggle
    {
        [EnumMember]
        Off = 0,
        [EnumMember]
        On = 1,
        [EnumMember]
        Toggle = 2
    }

    public class EventSubscriptions //: IWeakEventListener
    {
        public long _associatedID;

        public long AssociatedID
        {
            get { return _associatedID; }
            set
            {
                _associatedID = value;
                _logListener.AssociatedID = value;
            }
        }
        public Subscription Subscribed { get; set; }

        public EventSubscriptions()
        {
            _logListener = new EventListenerStub<LogMessage>();
            _runtimeLogListener = new EventListenerStub<LogMessage>();
            _inGameListener = new EventListenerStub<LogMessage>();
            _logListener.EventOccurred += new EventHandler<ObjectEventArgs<LogMessage>>(_logListener_EventOccurred);
            _runtimeLogListener.EventOccurred += new EventHandler<ObjectEventArgs<LogMessage>>(_runtimeLogListener_EventOccurred);
            _inGameListener.EventOccurred += new EventHandler<ObjectEventArgs<LogMessage>>(_inGameListener_EventOccurred);
        }

        void _inGameListener_EventOccurred(object sender, ObjectEventArgs<LogMessage> e)
        {
            GameChar chr = AssociatedID.Find() as GameChar;
            var msg = new NotificationMessageTextWithSource() { Text = e.obj.ToString(), Source = "In-Game Log" };
            var notify = new Notification(msg);
            chr.Notify(notify);
        }

        void _runtimeLogListener_EventOccurred(object sender, ObjectEventArgs<LogMessage> e)
        {
            GameChar chr = AssociatedID.Find() as GameChar;
            var msg = new NotificationMessageTextWithSource() { Text = e.obj.ToString(), Source = "Runtime Log" };
            var notify = new Notification(msg);
            chr.Notify(notify);
        }

        void _logListener_EventOccurred(object sender, ObjectEventArgs<LogMessage> e)
        {
            GameChar chr = AssociatedID.Find() as GameChar;
            var msg = new NotificationMessageTextWithSource() { Text = e.obj.ToString(), Source = "Log" };
            var notify = new Notification(msg);
            chr.Notify(notify);
        }

        public EventSubscriptions(long id) : this() { AssociatedID = id; _logListener.AssociatedID = id; }

        public EventListenerStub<LogMessage> _logListener;
        public EventListenerStub<LogMessage> _inGameListener;
        public EventListenerStub<LogMessage> _runtimeLogListener;

#if NO_SILVERLIGHT
        public WeakReference _logRef;
        public WeakReference _inGameRef;
        public WeakReference _runtimeLogRef;
        public WeakEventHandler _logHandler;
        public WeakEventHandler _inGameHandler;
        public WeakEventHandler _runtimeLogHandler;

        public bool IsSubscribed(Subscription subscription)
        {
            return (Subscribed & subscription) == subscription;
        }

        public void SubscribeToInGameEvents(OnOffToggle onOff)
        {
            if (_inGameRef == null)
                Interlocked.CompareExchange<WeakReference>(ref _inGameRef, new WeakReference(_inGameListener), null);

            if (!Subscribed.IsSet(Subscription.Log) && (onOff == OnOffToggle.On || onOff == OnOffToggle.Toggle))
            {
                _inGameHandler = WeakEventHandler.Register<ILogger, EventListenerStub<LogMessage>, ObjectEventArgs<LogMessage>>(
                    Globals.InGameLog, (s, eh) => s.LoggedMessage += eh, (s, eh) => s.LoggedMessage -= eh,
                    _inGameListener, (me, sender, ea) => me.OnEvent(sender, ea));

                Subscribed |= Subscription.InGameEvents;
            }
            else if (_inGameHandler != null && Subscribed.IsSet(Subscription.Log) && (onOff == OnOffToggle.Off || onOff == OnOffToggle.Toggle))
            {
                _inGameHandler.Deregister();

                Subscribed &= ~Subscription.InGameEvents;
            }
        }

        public void SubscribeToLogging(OnOffToggle onOff)
        {
            if (_logRef == null)
                Interlocked.CompareExchange<WeakReference>(ref _logRef, new WeakReference(_logListener), null);

            if (!Subscribed.IsSet(Subscription.Log) && (onOff == OnOffToggle.On || onOff == OnOffToggle.Toggle))
            {
                _logHandler = WeakEventHandler.Register<ILogger, EventListenerStub<LogMessage>, ObjectEventArgs<LogMessage>>(
                    GlobalLogger.Logger, (s, eh) => s.LoggedMessage += eh, (s, eh) => s.LoggedMessage -= eh,
                    _logListener, (me, sender, ea) => me.OnEvent(sender, ea));

                Subscribed |= Subscription.Log;
            }
            else if (_logHandler != null && Subscribed.IsSet(Subscription.Log) && (onOff == OnOffToggle.Off || onOff == OnOffToggle.Toggle))
            {
                _logHandler.Deregister();

                Subscribed &= ~Subscription.Log;
            }
        }

        public void SubscribeToRuntimeErrors(OnOffToggle onOff)
        {
            if (_runtimeLogRef == null)
                Interlocked.CompareExchange<WeakReference>(ref _runtimeLogRef, new WeakReference(_runtimeLogListener), null);

            if (!Subscribed.IsSet(Subscription.RuntimeErrors) && (onOff == OnOffToggle.On || onOff == OnOffToggle.Toggle))
            {
                _runtimeLogHandler = WeakEventHandler.Register<ILogger, EventListenerStub<LogMessage>, ObjectEventArgs<LogMessage>>(
                    RuntimeProblemLog.Instance._logger, (s, eh) => s.LoggedMessage += eh, (s, eh) => s.LoggedMessage -= eh,
                    _runtimeLogListener, (me, sender, ea) => me.OnEvent(sender, ea));

                Subscribed |= Subscription.RuntimeErrors;
            }
            else if (_runtimeLogHandler != null && Subscribed.IsSet(Subscription.Log) && (onOff == OnOffToggle.Off || onOff == OnOffToggle.Toggle))
            {
                _runtimeLogHandler.Deregister();

                Subscribed &= ~Subscription.RuntimeErrors;
            }
        }

        public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
        {
            //if (managerType == typeof(PropertyChangedEventManager))
            //{

            //}

            return true;
        }

        public void Subscribe(Subscription subscription, OnOffToggle subscribe)
        {
            if (subscription.IsSet(Subscription.Log))
                SubscribeToLogging(subscribe);

            if (subscription.IsSet(Subscription.RuntimeErrors))
                SubscribeToRuntimeErrors(subscribe);

            if (subscription.IsSet(Subscription.InGameEvents))
                SubscribeToInGameEvents(subscribe);
        }

        //public void Test()
        //{
        //    EventListenerStub<LogMessage> listener = new EventListenerStub<LogMessage>();
        //    WeakReference listenerRef = new WeakReference(listener);

        //    var weakHandler = WeakEventHandler.Register<ILogger, EventListenerStub<LogMessage>, ObjectEventArgs<LogMessage>>(
        //            Globals.InGameLog, (s, eh) => s.LoggedMessage += eh, (s, eh) => s.LoggedMessage -= eh,
        //            _inGameListener, (me, sender, ea) => me.OnEvent(sender, ea));

        //    listener = null;
        //    GC.Collect();

        //    //Assert.IsFalse(listenerRef.IsAlive);
        //}
#endif
    }
}
